"""
pytest配置和fixtures
"""

import asyncio
import pytest
import tempfile
import os
from typing import AsyncGenerator, Generator
from fastapi.testclient import TestClient
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
import redis.asyncio as redis

from app.main import app
from app.core.config import settings
from app.core.database import get_db, Base
from app.services.cache import cache_service


@pytest.fixture(scope="session")
def event_loop():
    """创建事件循环"""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest.fixture
def test_client() -> TestClient:
    """测试客户端"""
    return TestClient(app)


@pytest.fixture
async def test_db() -> AsyncGenerator[AsyncSession, None]:
    """测试数据库"""
    # 创建临时数据库
    db_fd, db_path = tempfile.mkstemp(suffix=".db")
    os.close(db_fd)
    
    try:
        # 创建数据库URL
        test_database_url = f"sqlite+aiosqlite:///{db_path}"
        
        # 创建引擎
        engine = create_async_engine(
            test_database_url,
            echo=False,
            connect_args={"check_same_thread": False}
        )
        
        # 创建会话
        async_session = sessionmaker(
            engine, class_=AsyncSession, expire_on_commit=False
        )
        
        # 创建表
        async with engine.begin() as conn:
            await conn.run_sync(Base.metadata.create_all)
        
        # 覆盖依赖
        async def get_test_db():
            async with async_session() as session:
                yield session
        
        app.dependency_overrides[get_db] = get_test_db
        
        # 提供会话
        async with async_session() as session:
            yield session
        
    finally:
        # 清理
        app.dependency_overrides.clear()
        if os.path.exists(db_path):
            os.unlink(db_path)


@pytest.fixture
async def test_redis() -> AsyncGenerator[redis.Redis, None]:
    """测试Redis"""
    # 创建Redis客户端
    redis_client = redis.from_url(
        "redis://localhost:6379/1",  # 使用数据库1
        encoding="utf-8",
        decode_responses=True
    )
    
    try:
        # 测试连接
        await redis_client.ping()
        
        # 覆盖缓存服务
        original_redis = cache_service.redis
        cache_service.redis = redis_client
        
        yield redis_client
        
    except Exception as e:
        pytest.skip(f"Redis not available: {e}")
    finally:
        # 恢复原始连接
        cache_service.redis = original_redis
        
        # 清理测试数据
        try:
            await redis_client.flushdb()
            await redis_client.close()
        except:
            pass


@pytest.fixture
def temp_audio_file() -> Generator[str, None, None]:
    """临时音频文件"""
    import wave
    import struct
    
    fd, temp_path = tempfile.mkstemp(suffix=".wav")
    os.close(fd)
    
    try:
        # 创建测试音频文件
        with wave.open(temp_path, 'w') as wav_file:
            wav_file.setnchannels(1)  # 单声道
            wav_file.setsampwidth(2)  # 16位
            wav_file.setframerate(44100)  # 44.1kHz
            
            # 生成1秒的测试音频（正弦波）
            duration = 1.0
            frequency = 440  # A4音符
            sample_rate = 44100
            samples = int(duration * sample_rate)
            
            for i in range(samples):
                value = int(32767 * (i / sample_rate) * frequency * 2 * 3.14159)
                wav_file.writeframes(struct.pack('<h', value))
        
        yield temp_path
        
    finally:
        if os.path.exists(temp_path):
            os.unlink(temp_path)


@pytest.fixture
def temp_text_file() -> Generator[str, None, None]:
    """临时文本文件"""
    fd, temp_path = tempfile.mkstemp(suffix=".txt")
    
    try:
        with os.fdopen(fd, 'w', encoding='utf-8') as f:
            f.write("这是一个测试文本。\n包含多行内容。\n用于测试语音合成功能。")
        
        yield temp_path
        
    finally:
        if os.path.exists(temp_path):
            os.unlink(temp_path)


@pytest.fixture
def sample_audiobook_data() -> dict:
    """示例有声读物数据 (模拟版本)"""
    from unittest.mock import Mock
    
    # 创建测试有声读物
    audiobook = Mock(
        id="audiobook_test_id",
        title="测试有声读物",
        author="测试作者",
        description="这是一个测试有声读物",
        language="zh-CN",
        user_id="test_user",
        file_path="/test/path",
        file_size=1024,
        duration=60.0,
        status="completed",
        chapters_count=3
    )
    
    # 创建测试章节
    chapters = []
    for i in range(3):
        chapter = Mock(
            id=f"chapter_test_id_{i}",
            audiobook_id=audiobook.id,
            title=f"第{i+1}章",
            content=f"这是第{i+1}章的内容。",
            order=i,
            duration=20.0,
            file_path=f"/test/chapter_{i+1}.wav",
            file_size=256,
            status="completed"
        )
        chapters.append(chapter)
    
    return {
        "audiobook": audiobook,
        "chapters": chapters
    }


@pytest.fixture
async def sample_voice_profile(test_db: AsyncSession) -> dict:
    """示例语音配置"""
    from app.models.tts import VoiceProfile
    
    voice_profile = VoiceProfile(
        name="测试语音",
        language="zh-CN",
        gender="female",
        age="adult",
        engine="pyttsx3",
        voice_id="test_voice_id",
        settings={
            "rate": 200,
            "volume": 1.0,
            "pitch": 1.0
        }
    )
    
    test_db.add(voice_profile)
    await test_db.commit()
    await test_db.refresh(voice_profile)
    
    return voice_profile.to_dict()


@pytest.fixture
def mock_tts_service():
    """模拟TTS服务"""
    from unittest.mock import Mock, AsyncMock
    from app.services.tts import TTSResult
    
    mock_service = Mock()
    
    # 模拟语音合成结果
    async def mock_synthesize(text, **kwargs):
        # 返回模拟音频数据
        audio_data = b"fake_audio_data_" + text.encode()
        return TTSResult(
            success=True,
            audio_data=audio_data,
            duration=len(text) * 0.1,
            format="wav"
        )
    
    mock_service.synthesize = AsyncMock(side_effect=mock_synthesize)
    mock_service.get_available_voices = AsyncMock(return_value=[
        Mock(id="voice1", name="Voice 1", language="zh-CN"),
        Mock(id="voice2", name="Voice 2", language="en-US")
    ])
    
    return mock_service


@pytest.fixture
def mock_audio_processor():
    """模拟音频处理器"""
    from unittest.mock import Mock, AsyncMock
    
    mock_processor = Mock()
    
    async def mock_convert_audio(input_path, output_path, **kwargs):
        # 模拟音频转换
        return True
    
    async def mock_get_audio_info(file_path):
        return Mock(
            duration=60.0,
            sample_rate=44100,
            channels=2,
            bit_rate=128000,
            format="wav"
        )
    
    mock_processor.convert_audio = AsyncMock(side_effect=mock_convert_audio)
    mock_processor.get_audio_info = AsyncMock(side_effect=mock_get_audio_info)
    
    return mock_processor


@pytest.fixture
def auth_headers() -> dict:
    """认证头"""
    return {
        "Authorization": "Bearer test_token",
        "Content-Type": "application/json"
    }

@pytest.fixture
def mock_auth():
    """模拟认证依赖"""
    from app.core.security import get_current_user
    from app.main import app
    
    # 创建模拟用户
    mock_user = {"sub": "test_user", "username": "testuser", "email": "test@example.com", "role": "user"}
    
    # 创建异步mock函数
    async def mock_get_current_user_dependency():
        return mock_user
    
    # 使用 FastAPI 的 dependency_overrides 机制
    app.dependency_overrides[get_current_user] = mock_get_current_user_dependency
    
    yield mock_user
    
    # 清理
    if get_current_user in app.dependency_overrides:
        del app.dependency_overrides[get_current_user]


@pytest.fixture
def webhook_payload():
    """Webhook负载示例"""
    return {
        "event": "test_event",
        "data": {
            "id": "test_id",
            "status": "completed"
        },
        "timestamp": "2024-01-01T00:00:00Z"
    }


# 测试工具函数
async def create_test_user(test_db: AsyncSession, user_id: str = "test_user") -> dict:
    """创建测试用户"""
    from app.models.user import User
    
    user = User(
        id=user_id,
        username="testuser",
        email="test@example.com",
        is_active=True
    )
    
    test_db.add(user)
    await test_db.commit()
    await test_db.refresh(user)
    
    return user.to_dict()


async def cleanup_test_data(test_db: AsyncSession, *models):
    """清理测试数据"""
    for model in models:
        if hasattr(model, 'delete'):
            await test_db.delete(model)
    
    await test_db.commit()


def assert_valid_uuid(uuid_string: str):
    """验证UUID格式"""
    import uuid
    
    try:
        uuid.UUID(uuid_string)
    except ValueError:
        pytest.fail(f"Invalid UUID: {uuid_string}")


def assert_valid_datetime(datetime_string: str):
    """验证日期时间格式"""
    from datetime import datetime
    
    try:
        datetime.fromisoformat(datetime_string.replace('Z', '+00:00'))
    except ValueError:
        pytest.fail(f"Invalid datetime: {datetime_string}")


# 测试标记
def pytest_configure(config):
    """配置pytest标记"""
    config.addinivalue_line(
        "markers", "unit: marks tests as unit tests"
    )
    config.addinivalue_line(
        "markers", "integration: marks tests as integration tests"
    )
    config.addinivalue_line(
        "markers", "slow: marks tests as slow running"
    )
    config.addinivalue_line(
        "markers", "redis: marks tests that require Redis"
    )
    config.addinivalue_line(
        "markers", "audio: marks tests that process audio files"
    )


# 测试钩子
def pytest_collection_modifyitems(config, items):
    """修改测试收集"""
    # 为没有标记的测试添加标记
    for item in items:
        if not any(item.iter_markers()):
            item.add_marker(pytest.mark.unit)


# 测试配置
pytest_plugins = []

# 测试环境变量
os.environ.setdefault("TESTING", "1")
os.environ.setdefault("DATABASE_URL", "sqlite+aiosqlite:///test.db")
os.environ.setdefault("REDIS_URL", "redis://localhost:6379/1")
